﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using FCS.Communication;
using System.Threading;
using FCS.GlobalComponents;

namespace FCS.Pattern
{
    /// <summary>
    /// Acts as a common class for all controllers
    /// </summary>
    public abstract class Controller
    {
        protected FountainConnection.CPQClient _connection = new FountainConnection.CPQClient();
        protected string _code;
        protected CommandPriority _controllerPriority;
        protected EventLogWriter _eventLogWriter;
        protected Patterns _patterns = new Patterns(); // contains all of the patterns
        protected bool _patternIsRunning = true; // asks if the pattern is still running (changes when asked to stop)

        protected object _connectionMonitor = new object(); // controls access to the _connection
        protected object _monitor = new object();

        public Controller()
        {
        }

        /// <summary>
        /// Reads the patterns off the disk and loads them
        /// </summary>
        /// <param name="di">Directory of patterns</param>
        protected void ReadPatterns(DirectoryInfo di)
        {
            FileInfo[] rgFiles = di.GetFiles("*.ptrn");

            foreach (FileInfo f in rgFiles)
            {
                _patterns.Add(ReadPattern(f.DirectoryName + "\\" + f.Name));
            }
        }

        /// <summary>
        /// Replaces the pattern at the index with the new pattern
        /// </summary>
        /// <param name="p"></param>
        /// <param name="index"></param>
        public void LoadPatternAtIndex(Pattern p, int index)
        {
            // make sure there are enough pattern so that it can remove
            while (_patterns.Count <= index)
            {
                _patterns.Add(new Pattern());
            }

            _patterns.RemoveAt(index);
            _patterns.Insert(index, p);
        }

        /// <summary>
        /// Reads out the pattern from a file
        /// </summary>
        /// <param name="filename">Name of pattern file</param>
        /// <returns>Pattern</returns>
        public Pattern ReadPattern(String filename)
        {
            // read files out from system

            StreamReader sr = new StreamReader(filename);

            Pattern p = new Pattern();

            // read the file and parse out the pattern 
            while (!sr.EndOfStream)
            {
                // each line is a pattern unit
                PatternUnit pu = new PatternUnit();
                string tmp = sr.ReadLine();

                string[] split = tmp.Split(' ');

                int waitTime;

                // Valve Pattern
                if (split[0].ToUpper() == "ON" || split[0].ToUpper() == "OFF")
                {
                    // Set the state
                    pu.State = split[0].ToUpper();
                    // Add the valves
                    for (int i = 1; i < split.Length; i++)
                    {
                        pu.Valves.Add(split[i]);
                    }
                }
                else if (int.TryParse(split[0], out waitTime))
                {
                    pu.WaitTime = waitTime;
                }
                else
                {
                    string message = "Attempted to parse a bad pattern file: " + filename;
                    _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Error, message);
                    throw new InvalidDataException(message);
                }
                p.Add(pu);
            }

            p.Title = filename;

            sr.Close();
            sr.Dispose();

            return p;
        }

        /// <summary>
        /// Play a pattern at a spesific index
        /// </summary>
        /// <param name="index"></param>
        /// <param name="cp"></param>
        public void PlayPattern(int index, CommandPriority cp)
        {
            // make sure the pattern exists
            if (index < _patterns.Count)
                PlayPattern(_patterns.ElementAt(index), cp);

            lock (_connectionMonitor)
            {
                bool completed = false;
                while (!completed)
                {
                    try
                    {
                        _connection.ResetThreshold(cp, _code);
                        completed = true;
                    }
                    catch (Exception) { }
                }
            }
        }

        /// <summary>
        /// Play a pattern
        /// </summary>
        /// <param name="p">Pattern to be played</param>
        /// <param name="cp">Priority to be played at</param>
        protected void PlayPattern(Pattern p, CommandPriority cp)
        {
            // Run through each pattern unit
            foreach (PatternUnit pu in p)
            {

                // Sleep
                if (pu.WaitTime != -1)
                {
                    Thread.Sleep(pu.WaitTime * 1000);
                }
                else
                {
                    // make sure the connection wasn't terminated before trying to use it
                    if (_connection != null)
                    {
                        // Combine commands
                        List<string> cmd = new List<string>();
                        foreach (string s in pu.Valves)
                        {
                            cmd.Add(s);
                        }
                        foreach (string s in pu.Weirs)
                        {
                            cmd.Add(s);
                        }

                        bool isSent = false;
                        while (!isSent)
                        {
                            try
                            {
                                _connection.SendCommand(pu.State, cmd, cp, _code);
                                isSent = true;
                            }
                            catch (Exception) { }
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                lock (_monitor)
                {
                    if (!_patternIsRunning)
                        break;
                }
            }

            bool isReset = false;

            while (!isReset)
            {
                try
                {
                    _connection.ResetThreshold(cp, _code);
                    isReset = true;
                }
                catch (Exception) { }
            }
        }

        /// <summary>
        /// Stop the fountain and shut off all the valves
        /// </summary>
        public void SendStopPattern()
        {
            Pattern p = new Pattern();
            PatternUnit pu = new PatternUnit();
            List<string> valves = new List<string>();
            valves.Add("V*");
            valves.Add("H*");

            pu.State = "OFF";
            pu.Valves = valves;

            lock (_connectionMonitor)
            {
                bool isSent = false;
                if (!isSent)
                {
                    try
                    {
                        _connection.SendCommand("OFF", valves, _controllerPriority, _code);
                        isSent = true;
                    }
                    catch (Exception) { }
                }
            }
        }

        /// <summary>
        /// H1 will mark off the time every hour
        /// </summary>
        public void MarkTime()
        {
            DateTime now = DateTime.Now;
            if (now.Minute == 59)
            {
                Thread.Sleep(60000);
                SendStopPattern(); // stop the fountain

                List<string> cmd = new List<string>();
                cmd.Add("H1");

                // for each hour, turn on and off H1
                for (int i = 0; i < now.Hour; i++)
                {
                    bool commandSent = false;
                    while (!commandSent)
                    {
                        try
                        {
                            _connection.SendCommand("ON", cmd, _controllerPriority, _code);
                            Thread.Sleep(2000);
                            _connection.SendCommand("OFF", cmd, _controllerPriority, _code);
                            Thread.Sleep(1500);
                            commandSent = true;
                        }
                        catch (Exception) { }
                    }
                }

                // resume
                bool isReset = false;
                while (!isReset)
                {
                    try
                    {
                        _connection.ResetThreshold(_controllerPriority, _code);
                        isReset = true;
                    }
                    catch (Exception) { }
                }
            }
        }

        /// <summary>
        /// Reset the CPQ threshold back to pattern
        /// </summary>
        /// <param name="cp"></param>
        public void ResetThreshold(CommandPriority cp)
        {
            bool isReset = false;
            while (!isReset)
            {
                try
                {
                    _connection.ResetThreshold(cp, _code);
                    isReset = true;
                }
                catch (Exception) { }
            }
        }

        /// <summary>
        /// Signal the pattern to stop running
        /// </summary>
        public void Stop()
        {
            lock (_monitor)
            {
                _patternIsRunning = false;
            }
        }

        /// <summary>
        /// End the service by closing the connection0
        /// </summary>
        public void Shutdown()
        {
            lock (_connectionMonitor)
            {
                _connection.Close();
                _connection = null;
            }
        }

        /// <summary>
        /// Set up the cRIO's address
        /// </summary>
        protected void SetDefaultCRIOAddress()
        {
            bool isSent = false;
            while (!isSent)
            {
                try
                {
                    _connection.SetDefaultCRIOAddress(_code);
                    isSent = true;                              
                }                                                               
                catch (Exception) { }
            }
        }

        /// <summary>
        /// Add restrcited valves to priority level
        /// </summary>
        /// <param name="restricted"></param>
        /// <param name="cp"></param>
        public void AddRestrictedValves(List<string> restricted, CommandPriority cp)
        {
            bool isSent = false;
            while (!isSent)
            {
                try
                {
                    _connection.AddRestrictedValves(restricted, cp, _code);
                    isSent = true;
                }
                catch (Exception) { }
            }
        }
    }
}
